package com.sugar.subscribe.course.module.business.service.coachwallet;

import cn.hutool.core.collection.CollUtil;
import com.sugar.subscribe.course.framework.common.exception.ServiceException;
import com.sugar.subscribe.course.framework.common.pojo.PageResult;
import com.sugar.subscribe.course.framework.common.util.object.BeanUtils;
import com.sugar.subscribe.course.module.business.controller.admin.coachwallet.vo.CoachWalletPageReqVO;
import com.sugar.subscribe.course.module.business.controller.app.coachwallet.vo.AppCoachProfitHistoryReqVO;
import com.sugar.subscribe.course.module.business.controller.app.coachwallet.vo.AppCoachProfitHistoryRespVO;
import com.sugar.subscribe.course.module.business.controller.app.coachwallet.vo.AppCoachProfitRespVO;
import com.sugar.subscribe.course.module.business.controller.app.coachwallet.vo.AppCoachWalletRespVO;
import com.sugar.subscribe.course.module.business.dal.dataobject.coachwallet.CoachWalletDO;
import com.sugar.subscribe.course.module.business.dal.mysql.coachwallet.CoachWalletMapper;
import com.sugar.subscribe.course.module.business.dal.mysql.order.BizOrderMapper;
import com.sugar.subscribe.course.module.business.dal.redis.objlock.ObjectLockRedisDAO;
import com.sugar.subscribe.course.module.business.util.AmountUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.util.Collections;
import java.util.List;

import static com.sugar.subscribe.course.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.sugar.subscribe.course.module.business.enums.ErrorCodeConstants.*;

/**
 * 教练钱包 Service 实现类
 *
 * @author Ysugar
 */
@Slf4j
@Service
@Validated
public class CoachWalletServiceImpl implements CoachWalletService {

    @Resource
    private CoachWalletMapper coachWalletMapper;

    @Resource
    private BizOrderMapper orderMapper;

    @Resource
    private ObjectLockRedisDAO redisLockDAO;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void updateCoachWallet(Long coachId, boolean add, Integer amount) {
        // 针对于变动金额过低的，则不进行变动
        if (amount <= 0) {
            log.error("教练 [{}] 变动 {} 金额 低于零，因此执行！", coachId, amount);
            return;
        }

        redisLockDAO.lock(coachId, 60000L, () -> {
            try {
                // 获取教练钱包信息
                CoachWalletDO wallet = coachWalletMapper.selectOne(CoachWalletDO::getCoachId, coachId);
                if (null == wallet) {
                    throw exception(COACH_WALLET_NOT_EXISTS);
                }

                Integer balance = wallet.getBalance();
                Integer totalEarnings = wallet.getTotalEarnings();

                // 若是扣除，则判断余额是否足够
                if (!add) {
                    // 判断余额是否足够
                    if (balance < amount)
                        throw exception(COACH_WALLET_BALANCE_INSUFFICIENT);

                    // 计算新余额
                    balance -= amount;
                } else {
                    // 计算新余额
                    balance += amount;

                    // 计算累计收益
                    totalEarnings += amount;
                }

                // 更新余额
                coachWalletMapper.update(CoachWalletDO::getCoachId, coachId,
                        CoachWalletDO::getBalance, balance,
                        CoachWalletDO::getTotalEarnings, totalEarnings);
            } catch (ServiceException se) {
                throw se;
            } catch (Throwable e) {
                log.error("用户余额修改失败：", e);
                throw exception(COACH_WALLET_BALANCE_UPDATE_ERROR);
            }
        });
    }

    @Override
    public void deleteCoachWallet(Long id) {
        // 校验存在
        validateCoachWalletExists(id);
        // 删除
        coachWalletMapper.deleteById(id);
    }

    @Override
    public void deleteCoachWalletListByIds(List<Long> ids) {
        // 删除
        coachWalletMapper.deleteByIds(ids);
    }


    private void validateCoachWalletExists(Long id) {
        if (coachWalletMapper.selectById(id) == null) {
            throw exception(COACH_WALLET_NOT_EXISTS);
        }
    }

    @Override
    public CoachWalletDO getCoachWallet(Long id) {
        return coachWalletMapper.selectById(id);
    }

    @Override
    public CoachWalletDO getWalletByCoachId(Long coachId) {
        CoachWalletDO coachWalletDO = coachWalletMapper.selectOne(CoachWalletDO::getCoachId, coachId);
        if (coachWalletDO == null) {
            throw exception(COACH_WALLET_NOT_EXISTS);
        }
        return coachWalletDO;
    }

    @Override
    public PageResult<CoachWalletDO> getCoachWalletPage(CoachWalletPageReqVO pageReqVO) {
        return coachWalletMapper.selectPage(pageReqVO);
    }

    @Override
    public AppCoachWalletRespVO appGetCoachWallet(Long coachId) {
        // 获取教练钱包信息
        CoachWalletDO wallet = coachWalletMapper.selectOne(CoachWalletDO::getCoachId, coachId);
        if (wallet == null) {
            throw exception(COACH_WALLET_NOT_EXISTS);
        }

        AppCoachWalletRespVO respVO = new AppCoachWalletRespVO();

        // 转换金额单位
        respVO.setBalance(AmountUtil.centToYuan(wallet.getBalance()))
                .setTotalEarnings(AmountUtil.centToYuan(wallet.getTotalEarnings()));

        return respVO;
    }

    @Override
    public AppCoachProfitRespVO getCoachProfit(Long coachId, Integer type) {
        LocalDateTime beginTime;
        LocalDateTime endTime;

        // 类型（0今日；1昨日；2本月；3上月）
        switch (type){
            case 1:
                endTime = LocalDateTime.now();
                beginTime = endTime.with(LocalTime.MIN);
                break;
            case 2:
                endTime = LocalDateTime.now();
                beginTime = endTime.with(LocalTime.MIN).with(TemporalAdjusters.firstDayOfMonth());
                break;
            case 3:
                beginTime = LocalDateTime.now().with(LocalTime.MIN).with(TemporalAdjusters.firstDayOfMonth()).plusMonths(-1);
                endTime = beginTime.with(LocalTime.MAX).with(TemporalAdjusters.lastDayOfMonth());
                break;
            default:
                endTime = LocalDateTime.now();
                beginTime = endTime.with(LocalTime.MIN);
                break;
        }

        return coachWalletMapper.statisticsCoachProfit(coachId, beginTime, endTime);
    }

    @Override
    public PageResult<AppCoachProfitHistoryRespVO> getCoachProfitHistoryPage(Long coachId, AppCoachProfitHistoryReqVO reqVO) {
        Integer pageNo = reqVO.getPageNo();
        Integer pageSize = reqVO.getPageSize();

        int skipCount = (pageNo - 1) * pageSize;

        List<AppCoachProfitHistoryRespVO> respVOList = orderMapper.selectProfitHistoryDatePage(coachId, reqVO.getSearch(), skipCount, pageSize);

        if (CollUtil.isEmpty(respVOList))
            return new PageResult<>(Collections.emptyList(), 0L);

        // 获取每个日期下的数据
        respVOList.forEach(respVO
                -> respVO.setDataList(orderMapper.getCoachProfitHistoryList(coachId, reqVO.getSearch(), respVO.getDate())));

        return new PageResult<>(respVOList, orderMapper.selectProfitHistoryDatePageCount(coachId, reqVO.getSearch()));
    }
}